package logger

import (
	"bytes"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"go-toy/middleware/logger/common"
	loggerInterface "go-toy/middleware/logger/interf"
)

var zapLogger *zap.Logger

func init() {
	InitZapLogger()
}

func InitZapLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

	zapLogger = zap.New(core, zap.AddCaller()) // *zap.Logger
	// zapLogger = zap.New(core, zap.AddCaller()).Sugar() // *zap.SugaredLogger
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	// return zapcore.NewConsoleEncoder(encoderConfig) // for console output
	return zapcore.NewJSONEncoder(encoderConfig) // for json output
}

func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := common.RollingIntance.Build()
	return zapcore.AddSync(lumberJackLogger)
}

type ZapWriter struct {
}

func (z *ZapWriter) Handle() gin.HandlerFunc {
	return func(gc *gin.Context) {
		// TODO: write log to any where
		defer zapLogger.Sync()

		customWriter := common.ResponseWriter{
			ResponseWriter: gc.Writer,
			Body:           bytes.NewBufferString(""),
		}
		gc.Writer = &customWriter

		startTime := time.Now()
		gc.Next()
		endTime := time.Now()

		latency := endTime.Sub(startTime)
		statusCode := gc.Writer.Status()
		clientIP := gc.ClientIP()
		method := gc.Request.Method
		path := gc.Request.URL.Path
		responseBody := customWriter.Body.String()

		zapLogger.Info("Request log",
			zap.Int("status_code", statusCode),
			zap.Duration("latency", latency),
			zap.String("client_ip", clientIP),
			zap.String("method", method),
			zap.String("path", path),
			zap.String("response_body", responseBody),
		)
	}
}

var ZapWriterInstance loggerInterface.Writer = &ZapWriter{}
